/*
** $Id: lvm.c,v 2.155 2013/03/16 21:10:18 roberto Exp $
** Lua virtual machine
** See Copyright Notice in lua.h
*/


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define lvm_c
#define LUA_CORE

#include "lua.h"

#include "ldebug.h"
#include "ldo.h"
#include "lfunc.h"
#include "lgc.h"
#include "lobject.h"
#include "lopcodes.h"
#include "lstate.h"
#include "lstring.h"
#include "ltable.h"
#include "ltm.h"
#include "lvm.h"



/* limit for table tag-method chains (to avoid loops) */
#define MAXTAGLOOP	100


const TValue *luaV_tonumber (const TValue *obj, TValue *n) {
	lua_Number num;
	if (ttisnumber(obj)) return obj;
	if (ttisstring(obj) && luaO_str2d(svalue(obj), tsvalue(obj)->len, &num)) {
		setnvalue(n, num);
		return n;
	}
	else
		return NULL;
}


int luaV_tostring (lua_State *L, StkId obj) {
	if (!ttisnumber(obj))
		return 0;
	else {
		char s[LUAI_MAXNUMBER2STR];
		lua_Number n = nvalue(obj);
		int l = lua_number2str(s, n);
		setsvalue2s(L, obj, luaS_newlstr(L, s, l));
		return 1;
	}
}


static void traceexec (lua_State *L) {
	CallInfo *ci = L->ci;
	lu_byte mask = L->hookmask;
	int counthook = ((mask & LUA_MASKCOUNT) && L->hookcount == 0);
	if (counthook)
		resethookcount(L);  /* reset count */
	if (ci->callstatus & CIST_HOOKYIELD) {  /* called hook last time? */
		ci->callstatus &= ~CIST_HOOKYIELD;  /* erase mark */
		return;  /* do not call hook again (VM yielded, so it did not move) */
	}
	if (counthook)
		luaD_hook(L, LUA_HOOKCOUNT, -1);  /* call count hook */
	if (mask & LUA_MASKLINE) {
		Proto *p = ci_func(ci)->p;
		int npc = pcRel(ci->u.l.savedpc, p);
		int newline = getfuncline(p, npc);
		if (npc == 0 ||  /* call linehook when enter a new function, */
			ci->u.l.savedpc <= L->oldpc ||  /* when jump back (loop), or when */
			newline != getfuncline(p, pcRel(L->oldpc, p)))  /* enter a new line */
			luaD_hook(L, LUA_HOOKLINE, newline);  /* call line hook */
	}
	L->oldpc = ci->u.l.savedpc;
	if (L->status == LUA_YIELD) {  /* did hook yield? */
		if (counthook)
			L->hookcount = 1;  /* undo decrement to zero */
		ci->u.l.savedpc--;  /* undo increment (resume will increment it again) */
		ci->callstatus |= CIST_HOOKYIELD;  /* mark that it yielded */
		ci->func = L->top - 1;  /* protect stack below results */
		luaD_throw(L, LUA_YIELD);
	}
}


static void callTM (lua_State *L, const TValue *f, const TValue *p1,
					const TValue *p2, TValue *p3, int hasres) {
						ptrdiff_t result = savestack(L, p3);
						setobj2s(L, L->top++, f);  /* push function */
						setobj2s(L, L->top++, p1);  /* 1st argument */
						setobj2s(L, L->top++, p2);  /* 2nd argument */
						if (!hasres)  /* no result? 'p3' is third argument */
							setobj2s(L, L->top++, p3);  /* 3rd argument */
						/* metamethod may yield only when called from Lua code */
						luaD_call(L, L->top - (4 - hasres), hasres, isLua(L->ci));
						if (hasres) {  /* if has result, move it to its place */
							p3 = restorestack(L, result);
							setobjs2s(L, p3, --L->top);
						}
}


void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) {
	int loop;
	for (loop = 0; loop < MAXTAGLOOP; loop++) {
		const TValue *tm;
		if (ttistable(t)) {  /* `t' is a table? */
			Table *h = hvalue(t);
			const TValue *res = luaH_get(h, key); /* do a primitive get */
			if (!ttisnil(res) ||  /* result is not nil? */
				(tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */
					setobj2s(L, val, res);
					return;
			}
			/* else will try the tag method */
		}
		else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))
			luaG_typeerror(L, t, "index");
		if (ttisfunction(tm)) {
			callTM(L, tm, t, key, val, 1);
			return;
		}
		t = tm;  /* else repeat with 'tm' */
	}
	luaG_runerror(L, "loop in gettable");
}


void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) {
	int loop;
	for (loop = 0; loop < MAXTAGLOOP; loop++) {
		const TValue *tm;
		if (ttistable(t)) {  /* `t' is a table? */
			Table *h = hvalue(t);
			TValue *oldval = cast(TValue *, luaH_get(h, key));
			/* if previous value is not nil, there must be a previous entry
			in the table; moreover, a metamethod has no relevance */
			if (!ttisnil(oldval) ||
				/* previous value is nil; must check the metamethod */
					((tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL &&
					/* no metamethod; is there a previous entry in the table? */
					(oldval != luaO_nilobject ||
					/* no previous entry; must create one. (The next test is
					always true; we only need the assignment.) */
					(oldval = luaH_newkey(L, h, key), 1)))) {
						/* no metamethod and (now) there is an entry with given key */
						setobj2t(L, oldval, val);  /* assign new value to that entry */
						invalidateTMcache(h);
						luaC_barrierback(L, obj2gco(h), val);
						return;
			}
			/* else will try the metamethod */
		}
		else  /* not a table; check metamethod */
			if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
				luaG_typeerror(L, t, "index");
		/* there is a metamethod */
		if (ttisfunction(tm)) {
			callTM(L, tm, t, key, val, 0);
			return;
		}
		t = tm;  /* else repeat with 'tm' */
	}
	luaG_runerror(L, "loop in settable");
}


static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2,
					   StkId res, TMS event) {
						   const TValue *tm = luaT_gettmbyobj(L, p1, event);  /* try first operand */
						   if (ttisnil(tm))
							   tm = luaT_gettmbyobj(L, p2, event);  /* try second operand */
						   if (ttisnil(tm)) return 0;
						   callTM(L, tm, p1, p2, res, 1);
						   return 1;
}


static const TValue *get_equalTM (lua_State *L, Table *mt1, Table *mt2,
								  TMS event) {
									  const TValue *tm1 = fasttm(L, mt1, event);
									  const TValue *tm2;
									  if (tm1 == NULL) return NULL;  /* no metamethod */
									  if (mt1 == mt2) return tm1;  /* same metatables => same metamethods */
									  tm2 = fasttm(L, mt2, event);
									  if (tm2 == NULL) return NULL;  /* no metamethod */
									  if (luaV_rawequalobj(tm1, tm2))  /* same metamethods? */
										  return tm1;
									  return NULL;
}


static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2,
						 TMS event) {
							 if (!call_binTM(L, p1, p2, L->top, event))
								 return -1;  /* no metamethod */
							 else
								 return !l_isfalse(L->top);
}


static int l_strcmp (const TString *ls, const TString *rs) {
	const char *l = getstr(ls);
	size_t ll = ls->tsv.len;
	const char *r = getstr(rs);
	size_t lr = rs->tsv.len;
	for (;;) {
		int temp = strcoll(l, r);
		if (temp != 0) return temp;
		else {  /* strings are equal up to a `\0' */
			size_t len = strlen(l);  /* index of first `\0' in both strings */
			if (len == lr)  /* r is finished? */
				return (len == ll) ? 0 : 1;
			else if (len == ll)  /* l is finished? */
				return -1;  /* l is smaller than r (because r is not finished) */
			/* both strings longer than `len'; go on comparing (after the `\0') */
			len++;
			l += len; ll -= len; r += len; lr -= len;
		}
	}
}


int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) {
	int res;
	if (ttisnumber(l) && ttisnumber(r))
		return luai_numlt(L, nvalue(l), nvalue(r));
	else if (ttisstring(l) && ttisstring(r))
		return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0;
	else if ((res = call_orderTM(L, l, r, TM_LT)) < 0)
		luaG_ordererror(L, l, r);
	return res;
}


int luaV_lessequal (lua_State *L, const TValue *l, const TValue *r) {
	int res;
	if (ttisnumber(l) && ttisnumber(r))
		return luai_numle(L, nvalue(l), nvalue(r));
	else if (ttisstring(l) && ttisstring(r))
		return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0;
	else if ((res = call_orderTM(L, l, r, TM_LE)) >= 0)  /* first try `le' */
		return res;
	else if ((res = call_orderTM(L, r, l, TM_LT)) < 0)  /* else try `lt' */
		luaG_ordererror(L, l, r);
	return !res;
}


/*
** equality of Lua values. L == NULL means raw equality (no metamethods)
*/
int luaV_equalobj_ (lua_State *L, const TValue *t1, const TValue *t2) {
	const TValue *tm;
	lua_assert(ttisequal(t1, t2));
	switch (ttype(t1)) {
	case LUA_TNIL: return 1;
	case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2));
	case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2);  /* true must be 1 !! */
	case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2);
	case LUA_TLCF: return fvalue(t1) == fvalue(t2);
	case LUA_TSHRSTR: return eqshrstr(rawtsvalue(t1), rawtsvalue(t2));
	case LUA_TLNGSTR: return luaS_eqlngstr(rawtsvalue(t1), rawtsvalue(t2));
	case LUA_TUSERDATA: {
		if (uvalue(t1) == uvalue(t2)) return 1;
		else if (L == NULL) return 0;
		tm = get_equalTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, TM_EQ);
		break;  /* will try TM */
						}
	case LUA_TTABLE: {
		if (hvalue(t1) == hvalue(t2)) return 1;
		else if (L == NULL) return 0;
		tm = get_equalTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ);
		break;  /* will try TM */
					 }
	default:
		lua_assert(iscollectable(t1));
		return gcvalue(t1) == gcvalue(t2);
	}
	if (tm == NULL) return 0;  /* no TM? */
	callTM(L, tm, t1, t2, L->top, 1);  /* call TM */
	return !l_isfalse(L->top);
}


void luaV_concat (lua_State *L, int total) {
	lua_assert(total >= 2);
	do {
		StkId top = L->top;
		int n = 2;  /* number of elements handled in this pass (at least 2) */
		if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) {
			if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT))
				luaG_concaterror(L, top-2, top-1);
		}
		else if (tsvalue(top-1)->len == 0)  /* second operand is empty? */
			(void)tostring(L, top - 2);  /* result is first operand */
		else if (ttisstring(top-2) && tsvalue(top-2)->len == 0) {
			setobjs2s(L, top - 2, top - 1);  /* result is second op. */
		}
		else {
			/* at least two non-empty string values; get as many as possible */
			size_t tl = tsvalue(top-1)->len;
			char *buffer;
			int i;
			/* collect total length */
			for (i = 1; i < total && tostring(L, top-i-1); i++) {
				size_t l = tsvalue(top-i-1)->len;
				if (l >= (MAX_SIZET/sizeof(char)) - tl)
					luaG_runerror(L, "string length overflow");
				tl += l;
			}
			buffer = luaZ_openspace(L, &G(L)->buff, tl);
			tl = 0;
			n = i;
			do {  /* concat all strings */
				size_t l = tsvalue(top-i)->len;
				memcpy(buffer+tl, svalue(top-i), l * sizeof(char));
				tl += l;
			} while (--i > 0);
			setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl));
		}
		total -= n-1;  /* got 'n' strings to create 1 new */
		L->top -= n-1;  /* popped 'n' strings and pushed one */
	} while (total > 1);  /* repeat until only 1 result left */
}


void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
	const TValue *tm;
	switch (ttypenv(rb)) {
	case LUA_TTABLE: {
		Table *h = hvalue(rb);
		tm = fasttm(L, h->metatable, TM_LEN);
		if (tm) break;  /* metamethod? break switch to call it */
		setnvalue(ra, cast_num(luaH_getn(h)));  /* else primitive len */
		return;
					 }
	case LUA_TSTRING: {
		setnvalue(ra, cast_num(tsvalue(rb)->len));
		return;
					  }
	default: {  /* try metamethod */
		tm = luaT_gettmbyobj(L, rb, TM_LEN);
		if (ttisnil(tm))  /* no metamethod? */
			luaG_typeerror(L, rb, "get length of");
		break;
			 }
	}
	callTM(L, tm, rb, rb, ra, 1);
}


void luaV_arith (lua_State *L, StkId ra, const TValue *rb,
				 const TValue *rc, TMS op) {
					 TValue tempb, tempc;
					 const TValue *b, *c;
					 if ((b = luaV_tonumber(rb, &tempb)) != NULL &&
						 (c = luaV_tonumber(rc, &tempc)) != NULL) {
							 lua_Number res = luaO_arith(op - TM_ADD + LUA_OPADD, nvalue(b), nvalue(c));
							 setnvalue(ra, res);
					 }
					 else if (!call_binTM(L, rb, rc, ra, op))
						 luaG_aritherror(L, rb, rc);
}


/*
** check whether cached closure in prototype 'p' may be reused, that is,
** whether there is a cached closure with the same upvalues needed by
** new closure to be created.
*/
static Closure *getcached (Proto *p, UpVal **encup, StkId base) {
	Closure *c = p->cache;
	if (c != NULL) {  /* is there a cached closure? */
		int nup = p->sizeupvalues;
		Upvaldesc *uv = p->upvalues;
		int i;
		for (i = 0; i < nup; i++) {  /* check whether it has right upvalues */
			TValue *v = uv[i].instack ? base + uv[i].idx : encup[uv[i].idx]->v;
			if (c->l.upvals[i]->v != v)
				return NULL;  /* wrong upvalue; cannot reuse closure */
		}
	}
	return c;  /* return cached closure (or NULL if no cached closure) */
}


/*
** create a new Lua closure, push it in the stack, and initialize
** its upvalues. Note that the call to 'luaC_barrierproto' must come
** before the assignment to 'p->cache', as the function needs the
** original value of that field.
*/
static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base,
						 StkId ra) {
							 int nup = p->sizeupvalues;
							 Upvaldesc *uv = p->upvalues;
							 int i;
							 Closure *ncl = luaF_newLclosure(L, nup);
							 ncl->l.p = p;
							 setclLvalue(L, ra, ncl);  /* anchor new closure in stack */
							 for (i = 0; i < nup; i++) {  /* fill in its upvalues */
								 if (uv[i].instack)  /* upvalue refers to local variable? */
									 ncl->l.upvals[i] = luaF_findupval(L, base + uv[i].idx);
								 else  /* get upvalue from enclosing function */
									 ncl->l.upvals[i] = encup[uv[i].idx];
							 }
							 luaC_barrierproto(L, p, ncl);
							 p->cache = ncl;  /* save it on cache for reuse */
}


/*
** finish execution of an opcode interrupted by an yield
*/
void luaV_finishOp (lua_State *L) {
	CallInfo *ci = L->ci;
	StkId base = ci->u.l.base;
	Instruction inst = *(ci->u.l.savedpc - 1);  /* interrupted instruction */
	OpCode op = GET_OPCODE(inst);
	switch (op) {  /* finish its execution */
	case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV:
	case OP_MOD: case OP_POW: case OP_UNM: case OP_LEN:
	case OP_GETTABUP: case OP_GETTABLE: case OP_SELF: {
		setobjs2s(L, base + GETARG_A(inst), --L->top);
		break;
					  }
	case OP_LE: case OP_LT: case OP_EQ: {
		int res = !l_isfalse(L->top - 1);
		L->top--;
		/* metamethod should not be called when operand is K */
		lua_assert(!ISK(GETARG_B(inst)));
		if (op == OP_LE &&  /* "<=" using "<" instead? */
			ttisnil(luaT_gettmbyobj(L, base + GETARG_B(inst), TM_LE)))
			res = !res;  /* invert result */
		lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_JMP);
		if (res != GETARG_A(inst))  /* condition failed? */
			ci->u.l.savedpc++;  /* skip jump instruction */
		break;
				}
	case OP_CONCAT: {
		StkId top = L->top - 1;  /* top when 'call_binTM' was called */
		int b = GETARG_B(inst);      /* first element to concatenate */
		int total = cast_int(top - 1 - (base + b));  /* yet to concatenate */
		setobj2s(L, top - 2, top);  /* put TM result in proper position */
		if (total > 1) {  /* are there elements to concat? */
			L->top = top - 1;  /* top is one after last element (at top-2) */
			luaV_concat(L, total);  /* concat them (may yield again) */
		}
		/* move final result to final position */
		setobj2s(L, ci->u.l.base + GETARG_A(inst), L->top - 1);
		L->top = ci->top;  /* restore top */
		break;
					}
	case OP_TFORCALL: {
		lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_TFORLOOP);
		L->top = ci->top;  /* correct top */
		break;
					  }
	case OP_CALL: {
		if (GETARG_C(inst) - 1 >= 0)  /* nresults >= 0? */
			L->top = ci->top;  /* adjust results */
		break;
				  }
	case OP_TAILCALL: case OP_SETTABUP: case OP_SETTABLE:
		break;
	default: lua_assert(0);
	}
}



/*
** some macros for common tasks in `luaV_execute'
*/

#if !defined luai_runtimecheck
#define luai_runtimecheck(L, c)		/* void */
#endif


#define RA(i)	(base+GETARG_A(i))
/* to be used after possible stack reallocation */
#define RB(i)	check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i))
#define RC(i)	check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i))
#define RKB(i)	check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \
	ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i))
#define RKC(i)	check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \
	ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i))
#define KBx(i)  \
	(k + (GETARG_Bx(i) != 0 ? GETARG_Bx(i) - 1 : GETARG_Ax(*ci->u.l.savedpc++)))


/* execute a jump instruction */
#define dojump(ci,i,e) \
{ int a = GETARG_A(i); \
	if (a > 0) luaF_close(L, ci->u.l.base + a - 1); \
	ci->u.l.savedpc += GETARG_sBx(i) + e; }

/* for test instructions, execute the jump instruction that follows it */
#define donextjump(ci)	{ i = *ci->u.l.savedpc; dojump(ci, i, 1); }


#define Protect(x)	{ {x;}; base = ci->u.l.base; }

#define checkGC(L,c)  \
	Protect( luaC_condGC(L,{L->top = (c);  /* limit of live values */ \
	luaC_step(L); \
	L->top = ci->top;})  /* restore top */ \
	luai_threadyield(L); )


#define arith_op(op,tm) { \
	TValue *rb = RKB(i); \
	TValue *rc = RKC(i); \
	if (ttisnumber(rb) && ttisnumber(rc)) { \
	lua_Number nb = nvalue(rb), nc = nvalue(rc); \
	setnvalue(ra, op(L, nb, nc)); \
	} \
		else { Protect(luaV_arith(L, ra, rb, rc, tm)); } }


#define vmdispatch(o)	switch(o)
#define vmcase(l,b)	case l: {b}  break;
#define vmcasenb(l,b)	case l: {b}		/* nb = no break */

void luaV_execute (lua_State *L) {
	CallInfo *ci = L->ci;
	LClosure *cl;
	TValue *k;
	StkId base;
newframe:  /* reentry point when frame changes (call/return) */
	lua_assert(ci == L->ci);
	cl = clLvalue(ci->func);
	k = cl->p->k;
	base = ci->u.l.base;
	/* main loop of interpreter */
	for (;;) {
		Instruction i = *(ci->u.l.savedpc++);
		StkId ra;
		if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) &&
			(--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) {
				Protect(traceexec(L));
		}
		/* WARNING: several calls may realloc the stack and invalidate `ra' */
		ra = RA(i);
		lua_assert(base == ci->u.l.base);
		lua_assert(base <= L->top && L->top < L->stack + L->stacksize);
		vmdispatch (GET_OPCODE(i)) {
			vmcase(OP_MOVE,
				setobjs2s(L, ra, RB(i));
			)
				vmcase(OP_LOADK,
				TValue *rb = k + GETARG_Bx(i);
			setobj2s(L, ra, rb);
			)
				vmcase(OP_LOADKX,
				TValue *rb;
			lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG);
			rb = k + GETARG_Ax(*ci->u.l.savedpc++);
			setobj2s(L, ra, rb);
			)
				vmcase(OP_LOADBOOL,
				setbvalue(ra, GETARG_B(i));
			if (GETARG_C(i)) ci->u.l.savedpc++;  /* skip next instruction (if C) */
			)
				vmcase(OP_LOADNIL,
				int b = GETARG_B(i);
			do {
				setnilvalue(ra++);
			} while (b--);
			)
				vmcase(OP_GETUPVAL,
				int b = GETARG_B(i);
			setobj2s(L, ra, cl->upvals[b]->v);
			)
				vmcase(OP_GETTABUP,
				int b = GETARG_B(i);
			Protect(luaV_gettable(L, cl->upvals[b]->v, RKC(i), ra));
			)
				vmcase(OP_GETTABLE,
				Protect(luaV_gettable(L, RB(i), RKC(i), ra));
			)
				vmcase(OP_SETTABUP,
				int a = GETARG_A(i);
			Protect(luaV_settable(L, cl->upvals[a]->v, RKB(i), RKC(i)));
			)
				vmcase(OP_SETUPVAL,
				UpVal *uv = cl->upvals[GETARG_B(i)];
			setobj(L, uv->v, ra);
			luaC_barrier(L, uv, ra);
			)
				vmcase(OP_SETTABLE,
				Protect(luaV_settable(L, ra, RKB(i), RKC(i)));
			)
				vmcase(OP_NEWTABLE,
				int b = GETARG_B(i);
			int c = GETARG_C(i);
			Table *t = luaH_new(L);
			sethvalue(L, ra, t);
			if (b != 0 || c != 0)
				luaH_resize(L, t, luaO_fb2int(b), luaO_fb2int(c));
			checkGC(L, ra + 1);
			)
				vmcase(OP_SELF,
				StkId rb = RB(i);
			setobjs2s(L, ra+1, rb);
			Protect(luaV_gettable(L, rb, RKC(i), ra));
			)
				vmcase(OP_ADD,
				arith_op(luai_numadd, TM_ADD);
			)
				vmcase(OP_SUB,
				arith_op(luai_numsub, TM_SUB);
			)
				vmcase(OP_MUL,
				arith_op(luai_nummul, TM_MUL);
			)
				vmcase(OP_DIV,
				arith_op(luai_numdiv, TM_DIV);
			)
				vmcase(OP_MOD,
				arith_op(luai_nummod, TM_MOD);
			)
				vmcase(OP_POW,
				arith_op(luai_numpow, TM_POW);
			)
				vmcase(OP_UNM,
				TValue *rb = RB(i);
			if (ttisnumber(rb)) {
				lua_Number nb = nvalue(rb);
				setnvalue(ra, luai_numunm(L, nb));
			}
			else {
				Protect(luaV_arith(L, ra, rb, rb, TM_UNM));
			}
			)
				vmcase(OP_NOT,
				TValue *rb = RB(i);
			int res = l_isfalse(rb);  /* next assignment may change this value */
			setbvalue(ra, res);
			)
				vmcase(OP_LEN,
				Protect(luaV_objlen(L, ra, RB(i)));
			)
				vmcase(OP_CONCAT,
				int b = GETARG_B(i);
			int c = GETARG_C(i);
			StkId rb;
			L->top = base + c + 1;  /* mark the end of concat operands */
			Protect(luaV_concat(L, c - b + 1));
			ra = RA(i);  /* 'luav_concat' may invoke TMs and move the stack */
			rb = b + base;
			setobjs2s(L, ra, rb);
			checkGC(L, (ra >= rb ? ra + 1 : rb));
			L->top = ci->top;  /* restore top */
			)
				vmcase(OP_JMP,
				dojump(ci, i, 0);
			)
				vmcase(OP_EQ,
				TValue *rb = RKB(i);
			TValue *rc = RKC(i);
			Protect(
				if (cast_int(equalobj(L, rb, rc)) != GETARG_A(i))
					ci->u.l.savedpc++;
				else
					donextjump(ci);
			)
				)
				vmcase(OP_LT,
				Protect(
				if (luaV_lessthan(L, RKB(i), RKC(i)) != GETARG_A(i))
					ci->u.l.savedpc++;
				else
					donextjump(ci);
			)
				)
				vmcase(OP_LE,
				Protect(
				if (luaV_lessequal(L, RKB(i), RKC(i)) != GETARG_A(i))
					ci->u.l.savedpc++;
				else
					donextjump(ci);
			)
				)
				vmcase(OP_TEST,
				if (GETARG_C(i) ? l_isfalse(ra) : !l_isfalse(ra))
					ci->u.l.savedpc++;
				else
					donextjump(ci);
			)
				vmcase(OP_TESTSET,
				TValue *rb = RB(i);
			if (GETARG_C(i) ? l_isfalse(rb) : !l_isfalse(rb))
				ci->u.l.savedpc++;
			else {
				setobjs2s(L, ra, rb);
				donextjump(ci);
			}
			)
				vmcase(OP_CALL,
				int b = GETARG_B(i);
			int nresults = GETARG_C(i) - 1;
			if (b != 0) L->top = ra+b;  /* else previous instruction set top */
			if (luaD_precall(L, ra, nresults)) {  /* C function? */
				if (nresults >= 0) L->top = ci->top;  /* adjust results */
				base = ci->u.l.base;
			}
			else {  /* Lua function */
				ci = L->ci;
				ci->callstatus |= CIST_REENTRY;
				goto newframe;  /* restart luaV_execute over new Lua function */
			}
			)
				vmcase(OP_TAILCALL,
				int b = GETARG_B(i);
			if (b != 0) L->top = ra+b;  /* else previous instruction set top */
			lua_assert(GETARG_C(i) - 1 == LUA_MULTRET);
			if (luaD_precall(L, ra, LUA_MULTRET))  /* C function? */
				base = ci->u.l.base;
			else {
				/* tail call: put called frame (n) in place of caller one (o) */
				CallInfo *nci = L->ci;  /* called frame */
				CallInfo *oci = nci->previous;  /* caller frame */
				StkId nfunc = nci->func;  /* called function */
				StkId ofunc = oci->func;  /* caller function */
				/* last stack slot filled by 'precall' */
				StkId lim = nci->u.l.base + getproto(nfunc)->numparams;
				int aux;
				/* close all upvalues from previous call */
				if (cl->p->sizep > 0) luaF_close(L, oci->u.l.base);
				/* move new frame into old one */
				for (aux = 0; nfunc + aux < lim; aux++)
					setobjs2s(L, ofunc + aux, nfunc + aux);
				oci->u.l.base = ofunc + (nci->u.l.base - nfunc);  /* correct base */
				oci->top = L->top = ofunc + (L->top - nfunc);  /* correct top */
				oci->u.l.savedpc = nci->u.l.savedpc;
				oci->callstatus |= CIST_TAIL;  /* function was tail called */
				ci = L->ci = oci;  /* remove new frame */
				lua_assert(L->top == oci->u.l.base + getproto(ofunc)->maxstacksize);
				goto newframe;  /* restart luaV_execute over new Lua function */
			}
			)
				vmcasenb(OP_RETURN,
				int b = GETARG_B(i);
			if (b != 0) L->top = ra+b-1;
			if (cl->p->sizep > 0) luaF_close(L, base);
			b = luaD_poscall(L, ra);
			if (!(ci->callstatus & CIST_REENTRY))  /* 'ci' still the called one */
				return;  /* external invocation: return */
			else {  /* invocation via reentry: continue execution */
				ci = L->ci;
				if (b) L->top = ci->top;
				lua_assert(isLua(ci));
				lua_assert(GET_OPCODE(*((ci)->u.l.savedpc - 1)) == OP_CALL);
				goto newframe;  /* restart luaV_execute over new Lua function */
			}
			)
				vmcase(OP_FORLOOP,
				lua_Number step = nvalue(ra+2);
			lua_Number idx = luai_numadd(L, nvalue(ra), step); /* increment index */
			lua_Number limit = nvalue(ra+1);
			if (luai_numlt(L, 0, step) ? luai_numle(L, idx, limit)
				: luai_numle(L, limit, idx)) {
					ci->u.l.savedpc += GETARG_sBx(i);  /* jump back */
					setnvalue(ra, idx);  /* update internal index... */
					setnvalue(ra+3, idx);  /* ...and external index */
			}
			)
				vmcase(OP_FORPREP,
				const TValue *init = ra;
			const TValue *plimit = ra+1;
			const TValue *pstep = ra+2;
			if (!tonumber(init, ra))
				luaG_runerror(L, LUA_QL("for") " initial value must be a number");
			else if (!tonumber(plimit, ra+1))
				luaG_runerror(L, LUA_QL("for") " limit must be a number");
			else if (!tonumber(pstep, ra+2))
				luaG_runerror(L, LUA_QL("for") " step must be a number");
			setnvalue(ra, luai_numsub(L, nvalue(ra), nvalue(pstep)));
			ci->u.l.savedpc += GETARG_sBx(i);
			)
				vmcasenb(OP_TFORCALL,
				StkId cb = ra + 3;  /* call base */
			setobjs2s(L, cb+2, ra+2);
			setobjs2s(L, cb+1, ra+1);
			setobjs2s(L, cb, ra);
			L->top = cb + 3;  /* func. + 2 args (state and index) */
			Protect(luaD_call(L, cb, GETARG_C(i), 1));
			L->top = ci->top;
			i = *(ci->u.l.savedpc++);  /* go to next instruction */
			ra = RA(i);
			lua_assert(GET_OPCODE(i) == OP_TFORLOOP);
			goto l_tforloop;
			)
				vmcase(OP_TFORLOOP,
l_tforloop:
			if (!ttisnil(ra + 1)) {  /* continue loop? */
				setobjs2s(L, ra, ra + 1);  /* save control variable */
				ci->u.l.savedpc += GETARG_sBx(i);  /* jump back */
			}
			)
				vmcase(OP_SETLIST,
				int n = GETARG_B(i);
			int c = GETARG_C(i);
			int last;
			Table *h;
			if (n == 0) n = cast_int(L->top - ra) - 1;
			if (c == 0) {
				lua_assert(GET_OPCODE(*ci->u.l.savedpc) == OP_EXTRAARG);
				c = GETARG_Ax(*ci->u.l.savedpc++);
			}
			luai_runtimecheck(L, ttistable(ra));
			h = hvalue(ra);
			last = ((c-1)*LFIELDS_PER_FLUSH) + n;
			if (last > h->sizearray)  /* needs more space? */
				luaH_resizearray(L, h, last);  /* pre-allocate it at once */
			for (; n > 0; n--) {
				TValue *val = ra+n;
				luaH_setint(L, h, last--, val);
				luaC_barrierback(L, obj2gco(h), val);
			}
			L->top = ci->top;  /* correct top (in case of previous open call) */
			)
				vmcase(OP_CLOSURE,
				Proto *p = cl->p->p[GETARG_Bx(i)];
			Closure *ncl = getcached(p, cl->upvals, base);  /* cached closure */
			if (ncl == NULL)  /* no match? */
				pushclosure(L, p, cl->upvals, base, ra);  /* create a new one */
			else
				setclLvalue(L, ra, ncl);  /* push cashed closure */
			checkGC(L, ra + 1);
			)
				vmcase(OP_VARARG,
				int b = GETARG_B(i) - 1;
			int j;
			int n = cast_int(base - ci->func) - cl->p->numparams - 1;
			if (b < 0) {  /* B == 0? */
				b = n;  /* get all var. arguments */
				Protect(luaD_checkstack(L, n));
				ra = RA(i);  /* previous call may change the stack */
				L->top = ra + n;
			}
			for (j = 0; j < b; j++) {
				if (j < n) {
					setobjs2s(L, ra + j, base - n + j);
				}
				else {
					setnilvalue(ra + j);
				}
			}
			)
				vmcase(OP_EXTRAARG,
				lua_assert(0);
			)
		}
	}
}

